/**
 * SMTP matchers.
 *
 * Sam Thorogood (c) 2010
 */

package main

import (
  "regexp"
  "strings"
)

var (
  baseCommand = regexp.MustCompile("^([a-zA-Z]+) +(.+)$")

  emailPart = regexp.MustCompile("^([a-zA-Z]*) *: *< *(.*) *>( +.*|)$")

  // Starts with a space, to be repeatedly matched on the last part of emailPart
  // TODO: Be less restrictive, and support no value or =.
  esmtpParam = regexp.MustCompile(" +([A-Za-z0-9][A-Za-z0-9\\-]*)=([^ ]*)")
)

func init() {
  if !emailPart.MatchString("FROM:<bob>") {
    panic("can't match")
  }
  p := esmtpParam.ExecuteString(" a=b  ")
  if len(p) == 0 || p[0] != 0 || p[1] != 4 || p[2] != 1 || p[3] != 2 || p[4] != 3 || p[5] != 4 {
    panic("can't match")
  }
}

/**
 * returns a list of substrings found by re, on s
 * if the re does not match, return an empty list
 */
func FindSubstrings(re *regexp.Regexp, s string) (result []string) {
  vals := re.ExecuteString(s)
  if len(vals) == 0 {
    return
  }

  result = make([]string, (len(vals) / 2) - 1)
  for i := 2; i < len(vals); i += 2 {
    result[(i/2)-1] = s[vals[i]:vals[i+1]]
  }
  return
}

/**
 * Match a SMTP-style command, i.e. "ehlo foo" or "ehlo".
 */
func MatchCommand(line string) (cmd string, rest string) {
  split := FindSubstrings(baseCommand, line)
  if len(split) == 2 {
    cmd = strings.TrimSpace(strings.ToLower(split[0]))
    rest = strings.TrimSpace(split[1])
  } else {
    cmd = strings.TrimSpace(strings.ToLower(line))
    rest = ""
  }
  return
}

/**
 * Matches an address format (i.e. "PREFIX:<address> param=foo param-more=bar").
 */
func MatchAddressFormat(prefix string, s string) (ok bool, address string, params map[string] string) {
  ok = false
  parts := FindSubstrings(emailPart, s)
  if len(parts) == 0 || strings.ToLower(parts[0]) != strings.ToLower(prefix) {
    return
  }

  params = make(map[string] string)
  rest := parts[2]
  for len(rest) > 0 {
    pos := esmtpParam.ExecuteString(rest)
    if len(pos) == 0 {
      return
    }
    key := rest[pos[2]:pos[3]]
    value := rest[pos[4]:pos[5]]
    params[key] = value
    rest = rest[pos[1]:]
  }

  ok = true
  address = parts[1]
  return
}
